/*-
 * SMSCommand.java
 *
 * $LastChangedBy: stein $
 * $LastChangedRevision: 98 $
 * $LastChangedDate: 2007-02-20 15:18:26 +0000 (Tue, 20 Feb 2007) $
 * $HeadURL: svn://localhost/SMS/src/com/googlecode/attention/sms/pdu/SMSCommand.java $
 *
 * Copyright (c) 2007, Stein Gunnar Bakkeby
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Stein Gunnar Bakkeby nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.googlecode.attention.sms.pdu;

import java.util.EnumSet;

import com.googlecode.attention.util.IntArrayBuilder;
import com.googlecode.attention.util.KeepingReference;

import static com.googlecode.attention.sms.pdu.PDUElement.*;

/** * SMS-COMMAND. * <BR> * <BR>Basic elements of the SMS-COMMAND type: * <BR> * <BR> * <TABLE BORDER="1"> * <TR><TD>Abbr.</TD><TD>Reference</TD><TD>P1)</TD><TD>R2)</TD><TD>Description</TD></TR> * <TR><TD>TP-MTI</TD><TD>TP-Message-Type-Indicator</TD><TD>M</TD><TD>2b</TD><TD>Parameter describing the type</TD></TR> * <TR><TD>TP-UDHI</TD><TD>TP-User-Data-Header-Indication</TD><TD>O</TD><TD>b</TD><TD>Parameter indicating that the TP-CD field contains a Header</TD></TR> * <TR><TD>TP-SRR</TD><TD>TP-Status-Report- Request</TD><TD>O</TD><TD>b</TD><TD>Parameter indicating if the SMS Command is requesting a status report.</TD></TR> * <TR><TD>TP-MR</TD><TD>TP-Message Reference</TD><TD>M</TD><TD>I</TD><TD>Parameter identifying the SMS-COMMAND</TD></TR> * <TR><TD>TP-PID</TD><TD>TP-Protocol- Identifier</TD><TD>M</TD><TD>o</TD><TD>Parameter identifying the above layer protocol, if any</TD></TR> * <TR><TD>TP-CT</TD><TD>TP-Command-Type</TD><TD>M</TD><TD>o</TD><TD>Parameter specifying which operation is to be performed on a SM</TD></TR> * <TR><TD>TP-MN</TD><TD>TP-Message-Number</TD><TD>M3)</TD><TD>o</TD><TD>Parameter indicating which SM in the SC to operate on</TD></TR> * <TR><TD>TP-DA</TD><TD>TP-Destination-Address</TD><TD>M4)</TD><TD>2-12o</TD><TD>Parameter indicating the Destination Address to which the TP-Command refers</TD></TR> * <TR><TD>TP-CDL</TD><TD>TP-Command-Data-Length</TD><TD>M</TD><TD>o</TD><TD>Parameter indicating the length of the TP-CD field in octets</TD></TR> * <TR><TD>TP-CD</TD><TD>TP-Command-Data</TD><TD>O</TD><TD>o</TD><TD>Parameter containing user data </TD></TR> * </TABLE> * <BR> * <BR>1) Provision: Mandatory (M) or Optional (O). * <BR>2) Representation: Integer (I), bit (b), 2bits (2b), octet (o). * <BR>3) For TP-Command-Types which are not for a specific SM this field * shall be ignored when received. Its value is of no concern but the field * must be present to maintain the structure. * <BR>4) For certain TP-Command-Types which operate on a specific SM (e.g. * Enquire, Delete etc.) the full TP-DA must be specified. For * TP-Command-Types which do not operate on a specific SM, the address length * must be set to zero indicating that the Address-Value fields are not * present. The Type-of-Address field must be present and shall be set to * zero and ignored. * <BR> * <BR>NOTE: The maximum guaranteed length of TP-CD is 146 octets. In order * to achieve the maximum stated above (156 octets), the TP-DA field must * have a length of 2 octets. *  * @author Stein Gunnar Bakkeby */
public class SMSCommand extends TPDU {
  /* Static field values */
  
  /* Field values */
  private int     fo  = 0;
  private int     mr  = 0;
  private int     pid = 0;
  private int     ct  = 0;
  private int     mn  = 0;
  private Address da = null;
  private CD cd = null;

  private EnumSet<PDUElement> pduElementSet = EnumSet.of(TP_FO,  TP_MN,
      TP_MR, TP_CT, TP_PID, TP_DA, TP_TOA, TP_NPI, TP_TON, TP_CDL, TP_CD);
  
  // TODO consider externalizable for TPDU
  /**
   * Constructor, creates the SMS-COMMAND based upon the given PDU.
   * 
   * @param pdu The raw data
   */
  public SMSCommand() {
    super(new int[13], TPDU.SMS_COMMAND);
  }
  
  /**
   * Constructor, creates the SMS-COMMAND based upon the given PDU.
   * 
   * @param pdu The raw data
   */
  public SMSCommand(final String pdu) {
    super(pdu, TPDU.SMS_COMMAND);
  }
  
  /**
   * Constructor, creates the SMS-COMMAND based upon the given PDU.
   * 
   * @param pdu The raw data
   */
  public SMSCommand(final int[] pdu) {
    super(pdu, TPDU.SMS_COMMAND);
  }
  
  /**
   * This will return an element of a given type.
   * 
   * @param type PDUElement values
   * @see PDUElement
   * @return The element in this TPDU
   */
  public int get(final PDUElement type) {
    switch (type) {
    case TP_FO:  return fo;
    case TP_MN:  return mn;
    case TP_MR:  return mr;
    case TP_CT:  return ct;
    case TP_PID: return pid;
    case TP_TOA: return (da == null ? 0 : da.getTOA().getValue());
    case TP_TON: return (da == null ? 0 : da.getTOA().getTypeOfNumber());
    case TP_NPI: return (da == null ? 0 : da.getTOA().getNumPlanIdent());
    case TP_CDL: return cd.getCDL();
    
    default:
      return -1;
    }
  }
  
  /**
   * This will return the command data.
   * 
   * @return The command data as an int array.
   */
  public CD getCommandData() {
    return cd;
  }
  
  /**
   * This will return the destination address.
   * 
   * @return The destination address as an int array.
   */
  public Address getDestinationAddress() {
    return da;
  }
  
  /**
   * This will set the destination address.
   * 
   * @param da The destination address
   */
  public void setDestinationAddress(final Address da) {
    this.da = da;
  }
  
  /**
   * This will set an element of a given type.
   * 
   * @param type See static field values
   * @param value The value of the element to set
   * @return True if PDU contains the given type, false otherwise
   */
  public boolean set(final PDUElement type, final int value) {
    switch (type) {
    case TP_FO:  fo  = value; break;
    case TP_MN:  mn  = value; break;
    case TP_MR:  mr  = value; break;
    case TP_CT:  ct  = value; break;
    case TP_PID: pid = value; break;
    
    default:
      return false;
    }
    return true;
  }
  
  /**
   * This will set an element of a given type.
   * 
   * @param type See static field values
   * @param value The value of the element to set
   * @return True if PDU contains the given type, false otherwise
   */
  public boolean set(final PDUElement type, final int[] value) {
    switch (type) {
    case TP_CD: cd = new CD(value); break;
    case TP_DA: da = new Address(value); break; // field
    
    default:
      return false;
    }
    return true;
  }
  
  /**
   * Throws UnsupportedOperationException as this object is being wrongly used.
   * 
   * @return The TP-User-Data
   * @see #getMessage()
   * @throws UnsupportedOperationException
   */
  public UD getUserData() {
    throw new UnsupportedOperationException(
    "Trying to use UD with a Command object.");
  }
  
  /**
   * Throws UnsupportedOperationException as this object is being wrongly used.
   * 
   * @return The TP-UDH
   * @throws UnsupportedOperationException
   */
  public UDH getUserDataHeader() {
    throw new UnsupportedOperationException(
        "Trying to use UDH with a Command object.");
  }
  
  /**
   * Throws UnsupportedOperationException as this object is being wrongly used.
   * 
   * @param userData The UD of this status report.
   * @throws UnsupportedOperationException
   */
  @KeepingReference
  public void setUserData(final UD userData) {
    throw new UnsupportedOperationException(
        "Trying to use UD with a Command object.");
  }
  
  /**
   * Throws UnsupportedOperationException as this object is being wrongly used.
   * 
   * @param userDataHeader The user data header to set.
   * @throws UnsupportedOperationException
   * @see UD#setUDH(UDH)
   */
  @KeepingReference
  public void setUserDataHeader(final UDH userDataHeader) {
    throw new UnsupportedOperationException(
        "Trying to use UDH with a Command object.");
  }
  
  /**
   * This will return true if the PDU contains the given type.
   * 
   * @param type See TPDU static field values
   * @return True if given type is present, false otherwise
   */
  public boolean contains(final PDUElement type) {
    return pduElementSet.contains(type);
  }
  
  /**
   * This will decode the SMS-COMMAND.
   */
  public void decode() {
    resetPDUPointer();
    this.fo  = extractOctet();
    this.mr  = extractOctet();
    this.pid = extractOctet();
    this.ct  = extractOctet();
    this.mn  = extractOctet();
    int dal = extractOctet();
    int toa = extractOctet();
    this.da = new Address(dal, new TOA(toa), extractOctets(dal));
    int cdl = extractOctet();
    this.cd = new CD(cdl > 0 ? extractOctets(cdl) : new int[0]);
  }
  
  /**
   * This will encode the SMS-COMMAND.
   */
  public void encode() {
    /* Avoid NullPointerException */
    if (da  == null) da  = new Address();
    if (cd  == null) cd  = new CD();

    final IntArrayBuilder output = new IntArrayBuilder(INIT_SIZE);

    output.append(fo);
    output.append(mr);
    output.append(pid);
    output.append(ct);
    output.append(mn);
    output.append(da.getAddressField());
    output.append(cd.getCDL());
    output.append(cd);
    
    if (rest != null && rest.length != 0) {
      output.append(rest);
    }
    
    super.pdu = output;
  }
  
  /**
   * @return [FO:&lt;fo&gt;][MR:&lt;mr&gt;][PID:&lt;pid&gt;][CT:&lt;ct&gt;][MN:&lt;mn&gt;][DA:&lt;address&gt;][CD:&lt;cd&gt;]
   */
  public String toString() {
    decode();
    final StringBuilder sb = new StringBuilder(256);
    sb.append("[FO:");
    sb.append(fo);
    sb.append("][MR:");
    sb.append(mr);
    sb.append("][PID:");
    sb.append(pid);
    sb.append("][CT:");
    sb.append(ct);
    sb.append("][MN:");
    sb.append(mn);
    sb.append("][DA:");
    sb.append(da);
    sb.append("][CD:");
    sb.append(cd);
    sb.append(']');
    return sb.toString();
  }

  /**
   * This will return the destination address. The method is equivalent of
   * the getDestinationAddress() method.
   * 
   * @return TP-DA
   * @see #getDestinationAddress()
   */
  @Override
  public Address getAddress() {
    return getDestinationAddress();
  }
  
  /**
   * This will set the destination address.
   * 
   * @param address The destination address
   * @see #setDestinationAddress(Address)
   */
  @Override
  public void setAddress(final Address address) {
    setDestinationAddress(address);
  }
}